#pragma once

#include <string>
#include <map>
#include <iostream>
#include <boost/lexical_cast.hpp>

extern "C"
{
#include "hugs.h"
}

#define toString(x)  boost::lexical_cast<string>(x)

using namespace std;

namespace Helper
{
	inline string setToHaskell(string set)
	{
		if (set == "BOOL")
			return "Bool";
		else if (set == "N")
			return "Integer";                           // TODO Muss noch in Haskell definiert werden
		else if (set == "Z")
			return "Integer";
		else if (set.back() != '*')
			return "Char";
		else
			return "String";
	}

	inline string haskell(string op)
	{
		if (op == "or")
			return "||";
		else if (op == "and") 
			return "&&";
		else if (op == "T")
			return "True";
		else if (op == "F")
			return "False";
		else if (op == "!")
			return "not";
		else if (op == "LAMBDA")
			return "[]";
		else
			return op;
	}


}

namespace Composite
{
	vector<string> operations;

	void init()
	{
		//operations.clear();
	}
}

struct OPNS 
{         
	string name;
	vector<string> parameters;
	string return_type;
};

namespace Global
{
	vector<string> operation_names;
	vector<OPNS> ops;
}

namespace Operations 
{
	vector<string> operation_names;
	vector<string> parameters;
	string return_type;

	void init()
	{
		operation_names.clear();
		parameters.clear();
		return_type = "";
	}

	void addName(string name)
	{
		operation_names.push_back(name);
		Global::operation_names.push_back(name);
	}

	void addParameter(string parameter)
	{
		parameters.push_back(Helper::setToHaskell(parameter));
	}


	void store()
	{
		struct OPNS p;
		p.name = operation_names.at(0);
		p.parameters = parameters;
		p.return_type = return_type;

		Global::ops.push_back(p);

		for (vector<string>::iterator it = operation_names.begin(); it != operation_names.end(); ++it)
		{
			string operation = *it + " :: "; 
			for (vector<string>::iterator ity = parameters.begin(); ity != parameters.end(); ++ity)
			{
				operation = operation + *ity + " -> ";
			}
			operation = operation + return_type + "\n";
			Composite::operations.push_back(operation);
		}
	} 

	void setReturnType(string type)
	{
		return_type = Helper::setToHaskell(type);
		store();
	}
}

struct Var
{
	string varname;
	string vartype;
};

namespace Variables 
{
	vector<Var> vars;
	Var v;
	vector<string> varnames;

	void init()
	{
		v.varname = "";
		v.vartype = "";
		varnames.clear();
	}

	void store()
	{
		for (int i=0;i<varnames.size();i++)
		{
			v.varname = varnames.at(i);
			vars.push_back(v);
			//cout << "Vars: " << v.varname << v.vartype << "\n";
		}
	}

	void addName(string varname)
	{
		varnames.push_back(varname);
	}

	void setType(string vartype)
	{
		v.vartype = Helper::setToHaskell(vartype);
		store();
	}

	string findType(string varname)
	{
		for (vector<Var>::iterator it = vars.begin(); it != vars.end(); ++it)
		{
			if ((*it).varname == varname)
				return (*it).vartype;
		}
		return "";
	}
}

namespace ConditionalEquations 
{
	string name;
	vector<string> parameters;
	vector<string> equation;
	vector<string> special_vars;

	int param_counter = 0;

	void init()
	{
		name = "";
		parameters.clear();
		equation.clear();
		param_counter = 0;
	}

	void setHead(string n)
	{
		name = n;
		bool found = false;
		for (vector<string>::iterator it = Global::operation_names.begin(); it != Global::operation_names.end(); ++it)
		{
			if ((*it) == n)
				found = true;
		}
		if (!found)
			cout << "Error: EQNS " << n << " was not declared in OPNS\n";
	}

	void addParameter(string param)
	{
		string paramtype;
		for (vector<OPNS>::iterator it = Global::ops.begin(); it != Global::ops.end(); ++it)
		{
			if ((*it).name == name)
				paramtype = (*it).parameters.at(param_counter);
		}

		// Check if param is a Char
		if ( paramtype == "String" && param != "LAMBDA" && Variables::findType(param) == "Char")
			param = "(" + toString(param) + ":[])";

		parameters.push_back(Helper::haskell(param));
		param_counter++;
	}

	void addParameters(string params) // yv ->  (y:yv)  ### xyv -> (x:y:yv) ### xyzv -> (x:y:z:zv) 
	{
		string x = "(";
		for (int i = 0; i< params.size()-1; i++)
		{
			x += toString(params.at(i)) + ":";
		}
		x += toString(params.at(params.size()-2)) + toString(params.at(params.size()-1));
		x += ")";
		parameters.push_back(x);
		special_vars.push_back( toString( toString(params.at(params.size()-2)) + toString(params.at(params.size()-1)) ));
		param_counter++;
	}

	void addTermElement(string element)
	{
		equation.push_back(Helper::haskell(element));
	}

	void addCall(string call)
	{
		equation.push_back("("+call);
	}

	
	

	void addCallParam(string call_param)
	{
		for (vector<string>::iterator itx = special_vars.begin(); itx != special_vars.end(); ++itx)
		{
			if ((*itx).at(1) == call_param.at(0))
				call_param = (*itx); 
		}
		equation.push_back(call_param);
	}

	void addCallParams(string call_params)
	{
		for (vector<string>::iterator itx = special_vars.begin(); itx != special_vars.end(); ++itx)
		{
			if ((*itx).at((*itx).size()-1) == call_params.at(call_params.size()-1))
				call_params = "(" + toString(call_params.at(0)) + ":" + (*itx) + ")"; 
		}
		equation.push_back(call_params);
	}

	void addCallContent(string call_content)
	{
		
	if (call_content.size() == 1)
	{
		addCallParam(call_content);

	}
	else if ( !(      (std::string::npos != call_content.find("-"))  
		|  (std::string::npos != call_content.find("+"))
		|  (std::string::npos != call_content.find("*")) 
		| (std::string::npos != call_content.find("/")) )  )
	{
		addCallParams(call_content);
	}
	else
	{
		equation.push_back("("+call_content+")");
	}

	}

	void addInteger(int param)
	{
		parameters.push_back(toString(param));
	}

	void closeCall()
	{
		equation.push_back(")");
	}

	void addNumber(int element)
	{
		equation.push_back(toString(element));
	}

	void store()
	{ 
		string eq = name + " ";
		for (vector<string>::iterator itx = parameters.begin(); itx != parameters.end(); ++itx)
		{
			eq = eq + *itx + " ";
		}
		eq = eq + "= ";
		for (vector<string>::iterator it = equation.begin(); it != equation.end(); ++it)
		{
			eq = eq + *it  + " ";
		}
		eq = eq + "\n";
		Composite::operations.push_back(eq);
		init();
	}
}


void clearAll()
{
	ConditionalEquations::parameters.clear();
	ConditionalEquations::equation.clear();
	ConditionalEquations::special_vars.clear();
	Variables::vars.clear();
	Variables::varnames.clear();
	Global::operation_names.clear();
	Global::ops.clear();
	Operations::operation_names.clear();
	Operations::parameters.clear();
	Operations::return_type.clear();
	Composite::operations.clear();
}
